/**
 * create by zhike
 * date:2015年3月2日
 */
package com.acooly.module.openapi.client.provider.baofu;

import com.acooly.core.utils.Reflections;
import com.acooly.core.utils.Strings;
import com.acooly.module.openapi.client.api.AbstractApiServiceClient;
import com.acooly.module.openapi.client.api.exception.ApiClientException;
import com.acooly.module.openapi.client.api.exception.ApiClientSocketTimeoutException;
import com.acooly.module.openapi.client.api.exception.ApiServerException;
import com.acooly.module.openapi.client.api.marshal.ApiMarshal;
import com.acooly.module.openapi.client.api.marshal.ApiUnmarshal;
import com.acooly.module.openapi.client.api.transport.Transport;
import com.acooly.module.openapi.client.provider.baofu.domain.BaoFuNotify;
import com.acooly.module.openapi.client.provider.baofu.domain.BaoFuRequest;
import com.acooly.module.openapi.client.provider.baofu.domain.BaoFuResponse;
import com.acooly.module.openapi.client.provider.baofu.exception.ApiClientProcessingException;
import com.acooly.module.openapi.client.provider.baofu.marshall.*;
import com.acooly.module.openapi.client.provider.baofu.marshall.info.ResponseInfo;
import com.acooly.module.openapi.client.provider.baofu.message.BaoFuAccountBalanceQueryResponse;
import com.acooly.module.openapi.client.provider.baofu.message.BaoFuBillDowloadRequest;
import com.acooly.module.openapi.client.provider.baofu.message.BaoFuBillDowloadResponse;
import com.acooly.module.openapi.client.provider.baofu.message.BaoFuWithdrawResponse;
import com.acooly.module.openapi.client.provider.baofu.support.BaoFuAlias;
import com.acooly.module.openapi.client.provider.baofu.utils.BaoFuSecurityUtil;
import com.acooly.module.openapi.client.provider.baofu.utils.StringHelper;
import com.acooly.module.openapi.client.provider.baofu.utils.XmlUtils;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;

/**
 * ApiService执行器
 *
 * @author zhike
 */
@Component("baofuApiServiceClient")
@Slf4j
public class BaoFuApiServiceClient
        extends AbstractApiServiceClient<BaoFuRequest, BaoFuResponse, BaoFuNotify, BaoFuNotify> {


    public static final String PROVIDER_NAME = "baofu";

    @Resource(name = "baoFuHttpTransport")
    private Transport transport;

    @Resource(name = "baoFuRequestMarshall")
    private BaoFuRequestMarshall requestMarshal;

    @Resource(name = "baoFuRedirectMarshall")
    private BaoFuRedirectMarshall redirectMarshal;

    @Resource(name = "baoFuSignMarshall")
    private BaoFuSignMarshall signMarshall;

    @Autowired
    private BaoFuResponseUnmarshall responseUnmarshal;
    @Autowired
    private BaoFuNotifyUnmarshall notifyUnmarshal;

    @Autowired
    private OpenAPIClientBaoFuProperties openAPIClientBaoFuProperties;

    @Override
    public BaoFuResponse execute(BaoFuRequest request) {
        try {
            beforeExecute(request);
            beforeCheckRequest(request);
            String url = request.getGatewayUrl();
            String requestMessage = getRequestMarshal().marshal(request);
            log.info("请求报文: {}", requestMessage);
            String responseMessage = getTransport().request(requestMessage, url).getBody();
            ResponseInfo responseInfo = new ResponseInfo();
            responseInfo.setMessage(responseMessage);
            responseInfo.setPartnerId(request.getMemberId());
            BaoFuResponse t = responseUnmarshal.unmarshal(responseInfo, request.getService());
            t.setMemberId(request.getMemberId());
            t.setService(request.getService());
            afterExecute(t);
            return t;
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            if(e instanceof ApiClientSocketTimeoutException || e instanceof ApiClientProcessingException) {
                throw  e;
            } else {
                throw new ApiClientException("内部错误:" + e.getMessage());
            }
        }
    }

    /**
     * 宝付提现
     * @param request
     * @return
     */
    public BaoFuResponse withdrawExecute(BaoFuRequest request) {
        try {
            beforeExecute(request);
            beforeCheckRequest(request);
            String url = request.getGatewayUrl();
            String requestMessage = requestMarshal.withdrawMarshal(request);
            log.info("请求报文: {}", requestMessage);
            String responseMessage = getTransport().request(requestMessage, url).getBody();
            ResponseInfo responseInfo = new ResponseInfo();
            responseInfo.setMessage(responseMessage);
            responseInfo.setPartnerId(request.getMemberId());
            BaoFuResponse t = responseUnmarshal.unmarshal(responseInfo, request.getService());
            t.setMemberId(request.getMemberId());
            t.setService(request.getService());
            afterExecute(t);
            return t;
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            if(e instanceof ApiClientSocketTimeoutException || e instanceof ApiClientProcessingException) {
                throw  e;
            } else {
                throw new ApiClientException("内部错误:" + e.getMessage());
            }
        }
    }

    /**
     * 宝付账户余额查询
     * @param request
     * @return
     */
    public BaoFuResponse accountBalanceQuery(BaoFuRequest request) {
        try {
            beforeExecute(request);
            beforeCheckRequest(request);
            String url = request.getGatewayUrl();
            String requestMessage = requestMarshal.accountBalanceQueryMarshal(request);
            log.info("请求报文: {}", requestMessage);
            String responseMessage = getTransport().request(requestMessage, url).getBody();
            log.info("响应报文：{}",responseMessage);
            ResponseInfo responseInfo = new ResponseInfo();
            responseInfo.setMessage(responseMessage);
            responseInfo.setPartnerId(request.getMemberId());
            BaoFuResponse t = XmlUtils.toBean(responseMessage, BaoFuAccountBalanceQueryResponse.class);
            t.setMemberId(request.getMemberId());
            t.setService(request.getService());
            afterExecute(t);
            return t;
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;

        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
            if(e instanceof ApiClientSocketTimeoutException || e instanceof ApiClientProcessingException) {
                throw  e;
            } else {
                throw new ApiClientException("内部错误:" + e.getMessage());
            }
        }
    }
    /**
     * 下载对账文件
     * @param request
     * @return
     */
    public BaoFuBillDowloadResponse billDownload(BaoFuRequest request) {
        BaoFuBillDowloadResponse response = new BaoFuBillDowloadResponse();
        String status = "failure";
        String resMessage = "下载对账文件失败";
        OutputStream outputStream = null;
        try {
            beforeExecute(request);
            BaoFuBillDowloadRequest billDownloadRequest = (BaoFuBillDowloadRequest) request;
            if (Strings.isBlank(request.getGatewayUrl())) {
                request.setGatewayUrl(openAPIClientBaoFuProperties.getGatewayUrl());
            }
            if(Strings.isBlank(request.getMemberId())) {
                request.setMemberId(openAPIClientBaoFuProperties.getPartnerId());
            }
            if(Strings.isBlank(billDownloadRequest.getFilePath())) {
                ((BaoFuBillDowloadRequest) request).setFilePath(openAPIClientBaoFuProperties.getFilePath());
            }
            String url = request.getGatewayUrl();
            SortedMap<String, String> billRequestMap = getBillRequestMap(request);
            String requestMessage = StringHelper.getRequestStr(billRequestMap);
            log.info("请求报文: {}", requestMessage);
            String responseMessage = getTransport().request(requestMessage, url).getBody();
            Map<String,String> resMap = StringHelper.getParamsMap(responseMessage);
            String respCode = resMap.get("resp_code");
            String tespMes = resMap.get("resp_msg");
            if(!Strings.equals(respCode,"0000")) {
                resMessage = tespMes;
            } else {
                String respBody = resMap.get("resp_body");
                if(Strings.isBlank(respBody)) {
                    resMessage = "报文实体为空";
                }else {
                    byte[] respBodyByte = BaoFuSecurityUtil.Base64DecodeByte(respBody);
                    InputStream dateInputStream = new ByteArrayInputStream(respBodyByte);
                    String filePath = billDownloadRequest.getFilePath();
                    if (Strings.isBlank(filePath)) {
                        filePath = openAPIClientBaoFuProperties.getFilePath();
                    }
                    String fileName = billDownloadRequest.getFileName();
                    String fileSuffix = billDownloadRequest.getFileSuffix();
                    if (Strings.isBlank(fileName)) {
                        fileName = billDownloadRequest.getSettleDate() + "." + fileSuffix;
                    } else {
                        fileName = fileName + "." + fileSuffix;
                    }
                    File billFile = new File(filePath + File.separator + fileName);
                    if (!billFile.exists()) {
                        billFile.getParentFile().mkdirs();
                    }
                    int count = 0;
                    while (count == 0) {
                        count = dateInputStream.available();
                    }
                    byte[] b = new byte[count];
                    billFile.createNewFile();
                    outputStream = new FileOutputStream(billFile);
                    dateInputStream.read(b);
                    outputStream.write(b);
                    outputStream.flush();
                    outputStream.close();
                    dateInputStream.close();
                    status = "success";
                    resMessage = "对账文件下载成功";
                }
            }
            log.info("账期settleDate={}对账文件下载完毕", billDownloadRequest.getSettleDate());
        } catch (ApiServerException ose) {
            log.error("服务器:" + ose.getMessage(), ose);
            throw ose;
        } catch (ApiClientException oce) {
            log.error("客户端:" + oce.getMessage(), oce);
            throw oce;
        } catch (Exception e) {
            log.error("内部错误:" + e.getMessage(), e);
        } finally {
            try {
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                log.info("输出流关闭失败:{}", e.getMessage());
            }
            response.setRespCode(status);
            response.setRespMsg(resMessage);
        }
        return response;
    }

    public BaoFuNotify notice(HttpServletRequest request, String serviceKey) {
        try {
            Map<String, String> notifyData = getSignMarshall().getDateMap(request);
            BaoFuNotify notify = getNoticeUnmarshal().unmarshal(notifyData, serviceKey);
            afterNotice(notify);
            return notify;
        } catch (ApiClientException oce) {
            log.warn("客户端:{}", oce.getMessage());
            throw oce;
        } catch (Exception e) {
            log.warn("内部错误:{}", e.getMessage());
            throw new ApiClientException("内部错误:" + e.getMessage());
        }
    }

    @Override
    protected ApiMarshal<String, BaoFuRequest> getRequestMarshal() {
        return this.requestMarshal;
    }

    @Override
    protected ApiUnmarshal<BaoFuNotify, Map<String, String>> getNoticeUnmarshal() {
        return this.notifyUnmarshal;
    }

    @Override
    protected ApiMarshal<String, BaoFuRequest> getRedirectMarshal() {
        return this.redirectMarshal;
    }

    @Override
    protected Transport getTransport() {
        return this.transport;
    }

    @Override
    protected ApiUnmarshal<BaoFuNotify, Map<String, String>> getReturnUnmarshal() {
        return this.notifyUnmarshal;
    }

    public BaoFuSignMarshall getSignMarshall() {
        return signMarshall;
    }

    public void setSignMarshall(BaoFuSignMarshall signMarshall) {
        this.signMarshall = signMarshall;
    }

    @Override
    public ApiUnmarshal<BaoFuResponse,String> getResponseUnmarshal() {
        return null;
    }

    public void setResponseUnmarshal(BaoFuResponseUnmarshall responseUnmarshal) {
        this.responseUnmarshal = responseUnmarshal;
    }

    @Override
    public String getName() {
        return PROVIDER_NAME;
    }

    /**
     * 获取对账请求map
     *
     * @param source
     * @return
     */
    protected SortedMap<String, String> getBillRequestMap(BaoFuRequest source) {
        SortedMap<String, String> signData = Maps.newTreeMap();
        Set<Field> fields = Reflections.getFields(source.getClass());
        String key = null;
        Object value = null;
        for (Field field : fields) {
            value = Reflections.getFieldValue(source, field.getName());
            if (value == null) {
                continue;
            }
            BaoFuAlias baofuAlias = field.getAnnotation(BaoFuAlias.class);
            if (baofuAlias == null) {
                continue;
            } else {
                if (!baofuAlias.sign()) {
                    continue;
                }
                key = baofuAlias.value();
            }
            if (Strings.isNotBlank((String) value)) {
                signData.put(key, (String) value);
            }
        }
        return signData;
    }

    /**
     * 请求前参数校验
     * @param request
     */
    private void beforeCheckRequest(BaoFuRequest request) {
        if (Strings.isBlank(request.getGatewayUrl())) {
            request.setGatewayUrl(openAPIClientBaoFuProperties.getGatewayUrl());
        }
        if(Strings.isBlank(request.getMemberId())) {
            request.setMemberId(openAPIClientBaoFuProperties.getPartnerId());
        }
        if(Strings.isBlank(request.getTerminalId())) {
            request.setTerminalId(openAPIClientBaoFuProperties.getTerminalId());
        }
    }
}
